package net.linlan.plat.handler;

import net.linlan.commons.core.StringUtils;
import net.linlan.annotation.AuthIgnore;
import net.linlan.frame.FramePubUser;
import net.linlan.frame.FrameToken;
import net.linlan.plat.comm.service.AuthService;
import net.linlan.plat.comm.utils.AuthReqUtils;
import net.linlan.plat.mbiz.ApiFieldConfig;
import net.linlan.sys.web.KernelConstant;
import net.linlan.sys.web.RedisService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * Filename:AuthorizationInterceptor.java
 * Desc:前台的权限(Token)验证，用于appa和appb等前台用户登录
 * 公众端的APP,入口为api/appm/,通过MemberUser拦截
 * 公众端的微信企业号等H5资源,入口为api/appm/h5,通过MemberUser拦截
 *
 * @author Linlan
 * CreateTime:2018/12/10 23:07
 * @version 1.0
 * @since 1.0
 */
@Component
public class ApiMemberInterceptor extends HandlerInterceptorAdapter {

    /**
     * 预处理回调方法，实现处理器的预处理（如检查登陆），第三个参数为响应的处理器，自定义Controller
     * 返回值：true表示继续流程（如调用下一个拦截器或处理器）；
     * false表示流程中断（如登录检查失败），不会继续调用其他的拦截器或处理器，此时我们需要通过response来产生响应；
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        //会员权限过滤，登录等方法需要非授权访问
        AuthIgnore annotation;
        if (handler instanceof HandlerMethod) {
            annotation = ((HandlerMethod) handler).getMethodAnnotation(AuthIgnore.class);
        } else {
            return true;
        }

        //如果有@AuthIgnore注解，则不验证token
        if (annotation != null) {
            return true;
        }

        //从request中获取token
        String token = AuthReqUtils.getToken(request);
        //token为空
        if (StringUtils.isBlank(token)) {
            try {
                response.sendError(ApiFieldConfig.FORBIDDEN, ApiFieldConfig.servCodeMap.get(ApiFieldConfig.FORBIDDEN));
            } catch (IOException e) {
                e.printStackTrace();
                return false;
            }
            return false;
        }
        //查询token信息
        FrameToken memberToken = authService.getByToken(token);
        if (memberToken == null || memberToken.getExpireTime().getTime() < System.currentTimeMillis()) {
            try {
                response.sendError(ApiFieldConfig.UNAUTHORIZED, ApiFieldConfig.servCodeMap.get(ApiFieldConfig.UNAUTHORIZED));
            } catch (IOException e) {
                e.printStackTrace();
                return false;
            }
            return false;
        }

        //设置memberId到request里，后续根据memberId，获取用户信息
        AuthReqUtils.setMemberId(request, memberToken.getId());
        //add 20200715 增加source的变量
        String source = AuthReqUtils.getByKey(request, AuthReqUtils.SOURCE_KEY);
        if (StringUtils.isBlank(source)) {
            source = "ssb";
        }
        AuthReqUtils.setByKey(request, AuthReqUtils.SOURCE_KEY, source);

        FramePubUser user = redisService.get(AuthReqUtils.REDIS_FRAME_USER_KEY + memberToken.getId(), FramePubUser.class);
        if (user == null) {
            user = authService.getWorkerByMemberId(memberToken.getId());
            if (user == null) {
                return false;
            }

            user.setSource(source);  //将平台来源放入source

            redisService.set(AuthReqUtils.REDIS_FRAME_USER_KEY + memberToken.getId(), user, KernelConstant.TEN_MINUTE_EXPIRE);

        }else{

        }
        return true;
    }

    /**
     * 后处理回调方法，实现处理器的后处理（但在渲染视图之前），此时我们可以通过modelAndView（模型和视图对象）对模型数据进行处理或对视图进行处理，modelAndView也可能为null。
     */
    @Override
    public void postHandle(HttpServletRequest request,
                           HttpServletResponse response, Object handler, ModelAndView mav)
            throws Exception {
        //系统启动时没有加入线程，此处仍然从Request内获取
        //从request内获取memberId
//        Long memberId = AuthReqUtils.getMemberId(request);
        //刷新用户活跃时间

    }

    /* (non-Javadoc)
     * Title:afterCompletion
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     * @see org.springframework.web.servlet.handler.HandlerInterceptorAdapter#afterCompletion(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.Object, java.lang.Exception)
     */

    /**
     * 整个请求处理完毕回调方法，即在视图渲染完毕时回调，如性能监控中我们可以在此记录结束时间并输出消耗时间，还可以进行一些资源清理，类似于try-catch-finally中的finally，但仅调用处理器执行链中
     */
    @Override
    public void afterCompletion(HttpServletRequest request,
                                HttpServletResponse response, Object handler, Exception ex) {
        // Sevlet容器有可能使用线程池，所以必须手动清空线程变量。
    }

    @Autowired
    private AuthService authService;
    @Autowired
    private RedisService redisService;

}
